Skip to content

TC-3753 advisory’s query and pruning functionality#2288

Merged
bxf12315 merged 2 commits intoguacsec:mainfrom
bxf12315:advisory-prune
Mar 17, 2026
Merged

TC-3753 advisory’s query and pruning functionality#2288
bxf12315 merged 2 commits intoguacsec:mainfrom
bxf12315:advisory-prune

Conversation

@bxf12315
Copy link
Contributor

@bxf12315 bxf12315 commented Mar 15, 2026

Summary by Sourcery

Add advisory management support to the CLI and centralize shared query/pruning and deletion logic for SBOMs and advisories.

New Features:

  • Introduce advisory CLI commands for listing and pruning advisories with filtering, pagination, and configurable output.
  • Expose advisory API client functions for listing and pruning advisories via the backend.

Enhancements:

  • Refactor common list, prune, and delete result types and logic into a shared module reused by SBOM and advisory operations.
  • Unify deletion progress handling and result formatting across SBOM and advisory prune and delete commands.
  • Update CLI documentation to cover new advisory list and prune commands, including example usage and output format.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Mar 15, 2026

Reviewer's Guide

This PR introduces shared list/prune/delete utilities in a new common module, refactors the SBOM API and commands to use these shared types and helpers, and adds new advisory API and CLI commands (list and prune) that reuse the same infrastructure, along with documentation updates for the new advisory functionality.

Sequence diagram for advisory prune command flow

sequenceDiagram
    actor User
    participant CliMain as main_rs
    participant Commands as Commands
    participant AdvCmd as AdvisoryCommands
    participant AdvApi as advisory_api
    participant Common as common_module
    participant Client as ApiClient

    User->>CliMain: invoke trustify advisory prune ...
    CliMain->>Commands: parse args to Advisory command
    Commands->>AdvCmd: run(ctx)

    AdvCmd->>AdvCmd: build PruneParams
    AdvCmd->>AdvApi: prune(&ctx.client, &params)

    AdvApi->>Common: build_prune_query(&params)
    Common-->>AdvApi: (query, ListParams)

    AdvApi->>AdvApi: log prune query
    AdvApi->>Client: get_with_query("/v2/advisory", &ListParams)
    Client-->>AdvApi: JSON response

    AdvApi->>AdvApi: parse JSON, build Vec<DeleteEntry>

    alt params.dry_run == true
        AdvApi->>Common: new_delete_result(total)
        Common-->>AdvApi: DeleteResult
    else params.dry_run == false
        AdvApi->>Common: delete_entries(&client, "/v2/advisory", entries, params.concurrency)
        Common->>Client: delete("/v2/advisory/{id}") for each entry
        Client-->>Common: delete result per entry
        Common-->>AdvApi: aggregated DeleteResult
    end

    AdvApi-->>AdvCmd: DeleteResult

    AdvCmd->>AdvCmd: print summary, write optional output file
    AdvCmd-->>Commands: ExitCode::SUCCESS
    Commands-->>CliMain: ExitCode::SUCCESS
    CliMain-->>User: command completed
Loading

Class diagram for shared common deletion utilities and consumers

classDiagram
    class ListParams {
        +Option~String~ q
        +Option~u32~ limit
        +Option~u32~ offset
        +Option~String~ sort
    }

    class PruneParams {
        +Option~String~ q
        +Option~u32~ limit
        +Option~DateTime_Local~ published_before
        +Option~i64~ older_than
        +Option~Vec~String~~ label
        +Option~u32~ keep_latest
        +bool dry_run
        +usize concurrency
    }

    class DeleteEntry {
        +String id
        +String identifier
    }

    class DeleteResult {
        +Vec~DeletedResult~ deleted
        +u32 deleted_total
        +Vec~SkippedResult~ skipped
        +u32 skipped_total
        +Vec~FailedResult~ failed
        +u32 failed_total
        +u32 total
    }

    class DeletedResult {
        +String id
        +String identifier
    }

    class SkippedResult {
        +String id
        +String identifier
    }

    class FailedResult {
        +String id
        +String identifier
        +String error
    }

    class CommonModule {
        +build_prune_query(params PruneParams) (String, ListParams)
        +delete_entries(client ApiClient, base_path String, entries Vec~DeleteEntry~, concurrency usize) DeleteResult
        +new_delete_result(total u32) DeleteResult
    }

    class ApiClient {
        +get_with_query(path String, params ListParams) String
        +delete(path String) Result
    }

    class SbomApi {
        +prune(client ApiClient, params PruneParams) DeleteResult
        +delete_list(client ApiClient, entries Vec~DeleteEntry~, concurrency usize) DeleteResult
        +delete_by_query(client ApiClient, query String, concurrency usize, dry_run bool) DeleteResult
    }

    class AdvisoryApi {
        +list(client ApiClient, params ListParams) String
        +prune(client ApiClient, params PruneParams) DeleteResult
    }

    class SbomCommands {
        +run(ctx Context) ExitCode
    }

    class AdvisoryCommands {
        +run(ctx Context) ExitCode
    }

    class Commands {
        +Sbom command
        +Advisory command
        +Auth command
        +run(ctx Context) ExitCode
    }

    CommonModule "1" -- "many" ListParams : uses
    CommonModule "1" -- "many" PruneParams : uses
    CommonModule "1" -- "many" DeleteEntry : uses
    CommonModule "1" -- "many" DeleteResult : creates

    DeleteResult "1" o-- "many" DeletedResult
    DeleteResult "1" o-- "many" SkippedResult
    DeleteResult "1" o-- "many" FailedResult

    SbomApi ..> CommonModule : uses
    AdvisoryApi ..> CommonModule : uses

    SbomCommands ..> SbomApi : calls
    AdvisoryCommands ..> AdvisoryApi : calls

    Commands ..> SbomCommands : dispatches
    Commands ..> AdvisoryCommands : dispatches
    Commands ..> ApiClient : holds in Context

    ApiClient <.. CommonModule : passed_in
    ApiClient <.. SbomApi : passed_in
    ApiClient <.. AdvisoryApi : passed_in
Loading

File-Level Changes

Change Details Files
Introduce a shared common module for list/prune parameters and bulk deletion utilities, standardizing delete results and identifiers.
  • Add ListParams and PruneParams structs used by both SBOM and advisory APIs for query and prune operations.
  • Add DeleteEntry, DeleteResult and related result types with generic id/identifier fields and serde support for reuse and JSON output.
  • Implement build_prune_query helper to construct prune queries and list parameters from PruneParams.
  • Implement delete_entries helper that performs concurrent DELETE calls against a base path with progress reporting and aggregates delete results.
  • Add new_delete_result helper to create empty DeleteResult instances for dry-run flows.
etc/trustify-cli/src/common/mod.rs
Refactor SBOM API and commands to use the shared common utilities for list/prune and deletion.
  • Replace SBOM-specific ListParams and PruneParams with shared types from the common module.
  • Update SBOM delete/prune flows to use the new DeleteEntry identifier field instead of document_id.
  • Replace local DeleteResult/DeletedResult/SkippedResult/FailedResult definitions with the shared common versions.
  • Simplify delete_list to delegate to delete_entries with the SBOM base path.
  • Use build_prune_query to construct prune list parameters and new_delete_result for dry-run responses.
  • Update SBOM command handlers to construct common ListParams and PruneParams instead of the SBOM module’s old types.
etc/trustify-cli/src/api/sbom.rs
etc/trustify-cli/src/commands/sbom.rs
Add advisory API module and CLI commands for listing and pruning advisories using the shared infrastructure.
  • Create advisory API module that exposes list and prune functions against the /v2/advisory endpoint using the shared ListParams, PruneParams, build_prune_query, delete_entries, and new_delete_result helpers.
  • In advisory prune, parse list responses, extract uuid and identifier fields into DeleteEntry structs, support dry-run, and defer to delete_entries for actual deletions.
  • Add AdvisoryCommands enum with List and Prune subcommands, including CLI flags for query, pagination, prune filters, concurrency, dry-run, output control, and quiet mode.
  • Implement advisory command execution to call the advisory API, format list output as pretty JSON, summarize prune results, and optionally write results to a JSON file.
  • Wire advisory commands into the main commands module and CLI dispatch by adding the advisory module and Advisory variant.
etc/trustify-cli/src/api/advisory.rs
etc/trustify-cli/src/commands/advisory.rs
etc/trustify-cli/src/commands/mod.rs
etc/trustify-cli/src/api/mod.rs
etc/trustify-cli/src/main.rs
Extend CLI documentation to cover the new advisory commands and the unified delete result format.
  • Update README to include sections for advisory list and advisory prune with example invocations and options.
  • Document the JSON output structure for prune operations using generic id/identifier fields that match the shared DeleteResult type.
etc/trustify-cli/README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@bxf12315 bxf12315 requested a review from ruromero March 15, 2026 13:16
@bxf12315
Copy link
Contributor Author

@ruromero In order to implement operations related to advisories, I refactored the existing code. Please review them.

@bxf12315 bxf12315 changed the title Advisory prune advisory’s query and pruning functionality Mar 15, 2026
Copy link
Contributor

@ruromero ruromero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The functional changes look good to me and seems a reasonable refactoring

@bxf12315 bxf12315 marked this pull request as ready for review March 16, 2026 14:04
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In AdvisoryCommands::Prune, the dry-run summary uses prune_result.deleted_total, but new_delete_result always sets this to 0 for dry runs; you probably want to report prune_result.total instead so the preview reflects how many advisories would be deleted.
  • The output argument for AdvisoryCommands::Prune has a default_value = "advisories.json", which means a results file is always written even when the flag is not specified; if you want output to be truly optional, drop the default and treat None as “don’t write a file.”
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `AdvisoryCommands::Prune`, the dry-run summary uses `prune_result.deleted_total`, but `new_delete_result` always sets this to 0 for dry runs; you probably want to report `prune_result.total` instead so the preview reflects how many advisories would be deleted.
- The `output` argument for `AdvisoryCommands::Prune` has a `default_value = "advisories.json"`, which means a results file is always written even when the flag is not specified; if you want output to be truly optional, drop the default and treat `None` as “don’t write a file.”

## Individual Comments

### Comment 1
<location path="etc/trustify-cli/src/commands/advisory.rs" line_range="147-151" />
<code_context>
+
+                let prune_result = advisory_api::prune(&ctx.client, &params).await?;
+
+                if !quiet {
+                    if *dry_run {
+                        println!(
+                            "[DRY-RUN] Would delete {} advisory(s)",
+                            prune_result.deleted_total
+                        );
+                    } else {
</code_context>
<issue_to_address>
**issue (bug_risk):** Dry-run summary prints `deleted_total` instead of the number of advisories that would be deleted

In dry-run mode `prune_result` comes from `new_delete_result(total)`, so `deleted_total` is always 0 while `total` is the number of candidates. Using `deleted_total` here will always print 0; this should instead use `prune_result.total` (or the underlying `total`) to show how many advisories would be deleted.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@bxf12315 bxf12315 changed the title advisory’s query and pruning functionality TC-3753 advisory’s query and pruning functionality Mar 16, 2026
@bxf12315 bxf12315 added this pull request to the merge queue Mar 17, 2026
Merged via the queue into guacsec:main with commit 6aa1b8c Mar 17, 2026
2 checks passed
@bxf12315 bxf12315 deleted the advisory-prune branch March 17, 2026 23:48
@github-project-automation github-project-automation bot moved this to Done in Trustify Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants